import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.geotools.data.*;
import org.geotools.data.shapefile.*;
import org.geotools.feature.simple.*;
import org.geotools.geojson.*;
import org.geotools.geojson.geom.*;
import org.geotools.referencing.crs.*;
import org.json.*;
import org.locationtech.jts.geom.*;
import org.opengis.feature.simple.*;


/**
 * 读取GeoJson数据，并写入到shp中
 */
public class ReadGeoJson2Shp {

    private final String pathGeoJson;

    public ReadGeoJson2Shp(String pPath_GeoJson) {
        this.pathGeoJson = pPath_GeoJson;
    }

    /**
     * 读取GeoJson数据，并写入新建的shp文件中
     * @throws Exception
     */
    public void readGeoJsonSource() throws Exception {
        // List<String> list = new ArrayList<String>();
//        FileInputStream finStream = new FileInputStream(pathGeoJson);
//        File file=new File(pathGeoJson);
        FileInputStream filein=new FileInputStream(pathGeoJson);
        // 防止路径乱码   如果utf-8 乱码  改GBK eclipse里创建的txt  用UTF-8，在电脑上自己创建的txt  用GBK
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(filein,"UTF-8"));
        GeometryJSON gjson = new GeometryJSON();
        List<JSONArray> jsonArrayList = getGeoJsonSource(bufferedReader);
        Class<?> geoType = getGeoType(jsonArrayList.get(0).getJSONObject(0));
        ShapefileDataStore dsShpNew = createShp(geoType);
        FeatureWriter<SimpleFeatureType, SimpleFeature> writerShp =
                dsShpNew.getFeatureWriter(dsShpNew.getTypeNames()[0], Transaction.AUTO_COMMIT);
        for (JSONArray features : jsonArrayList) {
            for (int idx = 0; idx < features.length(); idx++) {
                JSONObject infoTotal = features.getJSONObject(idx);
                JSONObject geoObj = infoTotal.getJSONObject("geometry");
                JSONObject props = infoTotal.getJSONObject("properties");
                String geoObjStr=String.valueOf(geoObj);
                //获取几何(这里必须是几何的Json形式，如果仅仅是坐标数组，则会报错)
                //这里有个问题：为什么前面一步判断了几何的类型，而这一步却直接使用readMultiPolygon呢？
                MultiPolygon geoNew = gjson.readMultiPolygon(GeoJSONUtil.toReader(geoObjStr));
                //创建新要素
                SimpleFeature featureNew = writerShp.next();
                featureNew.setAttribute("the_geom", geoNew);
                //赋值OID，类似于ArcGIS？
                featureNew.setAttribute("POIID", idx);
                //其他属性赋值
                featureNew.setAttribute("name", props.getString("name"));
                featureNew.setAttribute("adcode", props.getString("adcode"));
                featureNew.setAttribute("childrenNum", props.getString("childrenNum"));
                featureNew.setAttribute("level", props.getString("level"));
                writerShp.write();
            }
        }
        //关闭shp读写器
        writerShp.close();
        dsShpNew.dispose();
        //关闭文件读写流
        bufferedReader.close();
//        inStreamReader.close();
        filein.close();
    }

    /**
     * 获取原始的GeoJson数据组
     * @param pReader 文件流读取器
     * @return
     * @throws IOException
     */
    private List<JSONArray> getGeoJsonSource(BufferedReader pReader)throws IOException{
        List<JSONArray> result=new ArrayList<>();
        String line;
        JSONObject jsonRow, jsonProps;
        String geometry;
        //StringBuilder是线程不安全的；而StringBuffer是线程安全的，但是相应的效率会降低
        StringBuilder jsonBuilder=new StringBuilder();
        while ((line=pReader.readLine())!=null){
            String middleStr=line.replaceAll("\\u00A0+|\\s*|\t|\r|\n","");
            jsonBuilder.append(middleStr);
        }
        String testStr= jsonBuilder.toString();
        for (int idx = 0; idx < testStr.length(); idx++) {
            System.out.println(testStr.charAt(idx));
            System.out.println(Integer.valueOf(testStr.charAt(idx)));
            if(idx==9)
                break;
        }
        jsonRow = new JSONObject(jsonBuilder.toString());
        if (jsonRow != null) {
            result.add(jsonRow.getJSONArray("features"));
        }
        /*while ((line = pReader.readLine()) != null) {
            //这里读取的时候一定要注意换行符,如果有换行符的话那么应该使用stringbuilder组装，否则无法读取
            //每行小于100或者为空的跳过
            jsonBuilder.append(line);
            if (line == null || line.length() < 100)
                continue;
            if (line.endsWith(","))//将每行的逗号去除
                line = line.substring(0, line.length() - 1);
            //将每行的数据转换成json格式
            jsonRow = new JSONObject(line);
            if (jsonRow != null) {
                result.add(jsonRow.getJSONArray("features"));
            }
            *//*
            JSONArray features=jsonRow.getJSONArray("features");
            for (int idx = 0; idx < features.length(); idx++) {
                JSONObject info=features.getJSONObject(idx);
                JSONObject geo=info.getJSONObject("geometry");
                JSONArray arrayCoords=geo.getJSONArray("coordinates");
                System.out.println("几何一共有"+arrayCoords.length()+"个坐标");
                JSONObject props=info.getJSONObject("properties");
                System.out.println("几何："+geo.toString());
                System.out.println("属性集合："+props.toString());
                if (idx>3) {
                    break;
                }
            }
             *//*
        }*/
        return result;
    }

    /**
     * 获取要素的几何类型
     * @param pFeature GeoJson中的一个(一般是第一个)要素
     * @return 这里产生的类型是属于‘com.vividsolutions.jts’的包
     * @throws Exception 如果类型不对，那么则抛出‘几何类型异常’
     */
    private Class<?> getGeoType(JSONObject pFeature) throws Exception {
        String typeString= pFeature.getJSONObject("geometry").getString("type");
        Class<?> geoType;
        switch (typeString) {
            case "Point":
                geoType = Point.class;
                break;
            case "MultiPoint":
                geoType = MultiPoint.class;
                break;
            case "LineString":
                geoType = LineString.class;
                break;
            case "MultiLineString":
                geoType = MultiLineString.class;
                break;
            case "Polygon":
                geoType = Polygon.class;
                break;
            case "MultiPolygon":
                geoType = MultiPolygon.class;
                break;
            default:
                throw new Exception("Geometry类型错误");
        }
        return geoType;
    }

    /**
     * 创建shp文件并返回读写器
     * @param pGeoType 待处理的几何类型
     * @return
     * @throws Exception
     */
    private ShapefileDataStore createShp(Class<?> pGeoType) throws Exception{
        //创建新的shp文件(并不是打开一个已经存在的文件)
        //这里只能创建文件，不能同时创建路径和文件（即：如果路径不存在那么创建会失败）
        File newFile = new File(this.getClass().getResource("/").getFile() + "newshp.shp");
        Map<String,Serializable> parms= new HashMap<>();
        parms.put(ShapefileDataStoreFactory.URLP.key,newFile.toURI().toURL());
        parms.put("create spatial index", Boolean.TRUE);
        ShapefileDataStoreFactory factoryNew=new ShapefileDataStoreFactory();
        ShapefileDataStore dsShpNew=(ShapefileDataStore) factoryNew.createNewDataStore(parms);
        //定义图形和属性信息
        SimpleFeatureTypeBuilder tbNew=new SimpleFeatureTypeBuilder();
        tbNew.setCRS(DefaultGeographicCRS.WGS84);
        tbNew.setName("newshp");
        tbNew.add("the_geo",pGeoType);
        tbNew.add("POIID",Long.class);
        //如果有多个属性，那么必须在这里一个个添加（在建立新的shp文件时）；否则shp中是没有这个字段的
        tbNew.add("name",String.class);//省的名称
        tbNew.add("adcode",String.class);//邮编
        tbNew.add("childrenNum",String.class);
        tbNew.add("level",String.class);
        //这里一定要注意GeoTools的版本与JTS相对应，否则报错‘空指针’
        dsShpNew.createSchema(tbNew.buildFeatureType());
        //设置字符编码
        Charset charset = Charset.forName("GBK");
        dsShpNew.setCharset(charset);
        return dsShpNew;
    }
}
